/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 440149, 472654
* Patrik Suzzi <psuzzi@gmail.com> - Bug 496319, 498301
*******************************************************************************/
package org.eclipse.ui.internal.dialogs;
import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
import java.util.ArrayList;
import java.util.LinkedList;
import org.eclipse.core.runtime.IBundleGroup;
import org.eclipse.core.runtime.IBundleGroupProvider;
import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
import org.eclipse.ui.internal.ProductProperties;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.about.AboutBundleGroupData;
import org.eclipse.ui.internal.about.AboutFeaturesButtonManager;
import org.eclipse.ui.internal.about.AboutItem;
import org.eclipse.ui.internal.about.AboutTextManager;
import org.eclipse.ui.internal.about.InstallationDialog;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;
/**
* Displays information about the product.
*/
public class AboutDialog extends TrayDialog {
/**
*
*/
private static final String COPY_BUILD_ID_COMMAND = "org.eclipse.ui.ide.copyBuildIdCommand"; //$NON-NLS-1$
private final static int MAX_IMAGE_WIDTH_FOR_TEXT = 250;
private final static int DETAILS_ID = IDialogConstants.CLIENT_ID + 1;
private String productName;
private IProduct product;
private AboutBundleGroupData[] bundleGroupInfos;
private ArrayList<Image> images = new ArrayList<>();
private AboutFeaturesButtonManager buttonManager = new AboutFeaturesButtonManager();
private StyledText text;
private AboutTextManager aboutTextManager;
/**
* Create an instance of the AboutDialog for the given window.
* @param parentShell The parent of the dialog.
*/
public AboutDialog(Shell parentShell) {
super(parentShell);
product = Platform.getProduct();
if (product != null) {
productName = product.getName();
}
if (productName == null) {
productName = WorkbenchMessages.AboutDialog_defaultProductName;
}
// create a descriptive object for each BundleGroup
IBundleGroupProvider[] providers = Platform.getBundleGroupProviders();
LinkedList<AboutBundleGroupData> groups = new LinkedList<>();
if (providers != null) {
for (IBundleGroupProvider provider : providers) {
IBundleGroup[] bundleGroups = provider.getBundleGroups();
for (IBundleGroup bundleGroup : bundleGroups) {
groups.add(new AboutBundleGroupData(bundleGroup));
}
}
}
bundleGroupInfos = groups
.toArray(new AboutBundleGroupData[0]);
}
@Override
protected void buttonPressed(int buttonId) {
switch (buttonId) {
case DETAILS_ID:
BusyIndicator.showWhile(getShell().getDisplay(), () -> {
IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
InstallationDialog dialog = new InstallationDialog(getShell(), workbenchWindow);
dialog.setModalParent(AboutDialog.this);
dialog.open();
});
break;
default:
super.buttonPressed(buttonId);
break;
}
}
@Override
public boolean close() {
// dispose all images
for (int i = 0; i < images.size(); ++i) {
Image image = images.get(i);
image.dispose();
}
return super.close();
}
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText(NLS.bind(WorkbenchMessages.AboutDialog_shellTitle,productName ));
PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell,
IWorkbenchHelpContextIds.ABOUT_DIALOG);
}
/**
* Add buttons to the dialog's button bar.
*
* Subclasses should override.
*
* @param parent
* the button bar composite
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
createButton(parent, DETAILS_ID, WorkbenchMessages.AboutDialog_DetailsButton, false);
Label l = new Label(parent, SWT.NONE);
l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
GridLayout layout = (GridLayout) parent.getLayout();
layout.numColumns++;
layout.makeColumnsEqualWidth = false;
Button b = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.CLOSE_LABEL, true);
b.setFocus();
}
@Override
protected Control createDialogArea(Composite parent) {
// brand the about box if there is product info
Image aboutImage = null;
AboutItem item = null;
if (product != null) {
ImageDescriptor imageDescriptor = ProductProperties
.getAboutImage(product);
if (imageDescriptor != null) {
aboutImage = imageDescriptor.createImage();
}
// if the about image is small enough, then show the text
if (aboutImage == null
|| aboutImage.getBounds().width <= MAX_IMAGE_WIDTH_FOR_TEXT) {
String aboutText = ProductProperties.getAboutText(product);
if (aboutText != null) {
item = AboutTextManager.scan(aboutText);
}
}
if (aboutImage != null) {
images.add(aboutImage);
}
}
// create a composite which is the parent of the top area and the bottom
// button bar, this allows there to be a second child of this composite with
// a banner background on top but not have on the bottom
Composite workArea = new Composite(parent, SWT.NONE);
GridLayout workLayout = new GridLayout();
workLayout.marginHeight = 0;
workLayout.marginWidth = 0;
workLayout.verticalSpacing = 0;
workLayout.horizontalSpacing = 0;
workArea.setLayout(workLayout);
workArea.setLayoutData(new GridData(GridData.FILL_BOTH));
// page group
Color background = JFaceColors.getBannerBackground(parent.getDisplay());
Color foreground = JFaceColors.getBannerForeground(parent.getDisplay());
Composite top = (Composite) super.createDialogArea(workArea);
// override any layout inherited from createDialogArea
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
layout.horizontalSpacing = 0;
top.setLayout(layout);
top.setLayoutData(new GridData(GridData.FILL_BOTH));
top.setBackground(background);
top.setForeground(foreground);
// the image & text
final Composite topContainer = new Composite(top, SWT.NONE);
topContainer.setBackground(background);
topContainer.setForeground(foreground);
layout = new GridLayout();
layout.numColumns = (aboutImage == null || item == null ? 1 : 2);
layout.marginWidth = 0;
layout.marginHeight = 0;
layout.verticalSpacing = 0;
layout.horizontalSpacing = 0;
topContainer.setLayout(layout);
GC gc = new GC(parent);
// arbitrary default
int topContainerHeightHint = 100;
try {
// default height enough for 6 lines of text
topContainerHeightHint = Math.max(topContainerHeightHint, gc
.getFontMetrics().getHeight() * 6);
}
finally {
gc.dispose();
}
//image on left side of dialog
if (aboutImage != null) {
Label imageLabel = new Label(topContainer, SWT.NONE);
imageLabel.setBackground(background);
imageLabel.setForeground(foreground);
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.BEGINNING;
data.grabExcessHorizontalSpace = false;
imageLabel.setLayoutData(data);
imageLabel.setImage(aboutImage);
topContainerHeightHint = Math.max(topContainerHeightHint, aboutImage.getBounds().height);
}
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
data.verticalAlignment = GridData.FILL;
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
data.heightHint = topContainerHeightHint;
topContainer.setLayoutData(data);
if (item != null) {
final int minWidth = 400; // This value should really be calculated
// from the computeSize(SWT.DEFAULT,
// SWT.DEFAULT) of all the
// children in infoArea excluding the
// wrapped styled text
// There is no easy way to do this.
final ScrolledComposite scroller = new ScrolledComposite(topContainer,
SWT.V_SCROLL | SWT.H_SCROLL);
data = new GridData(GridData.FILL_BOTH);
data.widthHint = minWidth;
scroller.setLayoutData(data);
final Composite textComposite = new Composite(scroller, SWT.NONE);
textComposite.setBackground(background);
layout = new GridLayout();
textComposite.setLayout(layout);
text = new StyledText(textComposite, SWT.MULTI | SWT.WRAP | SWT.READ_ONLY);
// Don't set caret to 'null' as this causes https://bugs.eclipse.org/293263.
// text.setCaret(null);
text.setFont(parent.getFont());
text.setText(item.getText());
text.setCursor(null);
text.setBackground(background);
text.setForeground(foreground);
aboutTextManager = new AboutTextManager(text);
aboutTextManager.setItem(item);
createTextMenu();
GridData gd = new GridData();
gd.verticalAlignment = GridData.BEGINNING;
gd.horizontalAlignment = GridData.FILL;
gd.grabExcessHorizontalSpace = true;
text.setLayoutData(gd);
// Adjust the scrollbar increments
scroller.getHorizontalBar().setIncrement(20);
scroller.getVerticalBar().setIncrement(20);
final boolean[] inresize = new boolean[1]; // flag to stop unneccesary
// recursion
textComposite.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
if (inresize[0]) {
return;
}
inresize[0] = true;
// required because of bugzilla report 4579
textComposite.layout(true);
// required because you want to change the height that the
// scrollbar will scroll over when the width changes.
int width = textComposite.getClientArea().width;
Point p = textComposite.computeSize(width, SWT.DEFAULT);
scroller.setMinSize(minWidth, p.y);
inresize[0] = false;
}
});
scroller.setExpandHorizontal(true);
scroller.setExpandVertical(true);
Point p = textComposite.computeSize(minWidth, SWT.DEFAULT);
textComposite.setSize(p.x, p.y);
scroller.setMinWidth(minWidth);
scroller.setMinHeight(p.y);
scroller.setContent(textComposite);
}
// horizontal bar
Label bar = new Label(workArea, SWT.HORIZONTAL | SWT.SEPARATOR);
data = new GridData();
data.horizontalAlignment = GridData.FILL;
bar.setLayoutData(data);
// add image buttons for bundle groups that have them
Composite bottom = (Composite) super.createDialogArea(workArea);
// override any layout inherited from createDialogArea
layout = new GridLayout();
bottom.setLayout(layout);
data = new GridData();
data.horizontalAlignment = SWT.FILL;
data.verticalAlignment = SWT.FILL;
data.grabExcessHorizontalSpace = true;
bottom.setLayoutData(data);
createFeatureImageButtonRow(bottom);
// spacer
bar = new Label(bottom, SWT.NONE);
data = new GridData();
data.horizontalAlignment = GridData.FILL;
bar.setLayoutData(data);
return workArea;
}
/**
* Create the context menu for the text widget.
*
* @since 3.4
*/
private void createTextMenu() {
final MenuManager textManager = new MenuManager();
textManager.add(new CommandContributionItem(
new CommandContributionItemParameter(PlatformUI
.getWorkbench(), null, IWorkbenchCommandConstants.EDIT_COPY,
CommandContributionItem.STYLE_PUSH)));
textManager.add(new CommandContributionItem(
new CommandContributionItemParameter(PlatformUI
.getWorkbench(), null, COPY_BUILD_ID_COMMAND, CommandContributionItem.STYLE_PUSH)));
textManager.add(new CommandContributionItem(new CommandContributionItemParameter(PlatformUI
.getWorkbench(), null, IWorkbenchCommandConstants.EDIT_SELECT_ALL,
CommandContributionItem.STYLE_PUSH)));
text.setMenu(textManager.createContextMenu(text));
text.addDisposeListener(e -> textManager.dispose());
}
private void createFeatureImageButtonRow(Composite parent) {
Composite featureContainer = new Composite(parent, SWT.NONE);
RowLayout rowLayout = new RowLayout();
rowLayout.wrap = true;
featureContainer.setLayout(rowLayout);
GridData data = new GridData();
data.horizontalAlignment = GridData.FILL;
featureContainer.setLayoutData(data);
for (AboutBundleGroupData bundleGroupInfo : bundleGroupInfos) {
createFeatureButton(featureContainer, bundleGroupInfo);
}
}
private Button createFeatureButton(Composite parent,
final AboutBundleGroupData info) {
if (!buttonManager.add(info)) {
return null;
}
ImageDescriptor desc = info.getFeatureImage();
Image featureImage = null;
Button button = new Button(parent, SWT.FLAT | SWT.PUSH);
button.setData(info);
featureImage = desc.createImage();
images.add(featureImage);
button.setImage(featureImage);
button.setToolTipText(info.getProviderName());
button.getAccessible().addAccessibleListener(new AccessibleAdapter(){
@Override
public void getName(AccessibleEvent e) {
e.result = info.getProviderName();
}
});
button.addSelectionListener(widgetSelectedAdapter(event -> {
AboutBundleGroupData[] groupInfos = buttonManager.getRelatedInfos(info);
AboutBundleGroupData selection = (AboutBundleGroupData) event.widget.getData();
AboutFeaturesDialog d = new AboutFeaturesDialog(getShell(), productName, groupInfos, selection);
d.open();
}));
return button;
}
@Override
protected boolean isResizable() {
return true;
}
}